package com.hmall.search.service.Impl;

import com.alibaba.fastjson.JSON;
import com.hmall.common.client.SearchFeignClient;
import com.hmall.common.dto.Item;
import com.hmall.common.dto.PageDTO;
import com.hmall.search.pojo.ItemDoc;
import com.hmall.search.pojo.RequestParams;
import com.hmall.search.service.SearchService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    RestHighLevelClient client;

    @Autowired
    SearchFeignClient feignClient;


    @Override
    public void insertById(Long id) {
        try {
            //调用feign  获取数据库的数据
            Item item = feignClient.findItemById(id);
            //转itemDoc对象
            ItemDoc itemDoc = new ItemDoc(item);
            //建立请求对象
            IndexRequest request = new IndexRequest("item").id(itemDoc.getId().toString());
            //添加请求参数
            request.source(JSON.toJSONString(itemDoc),XContentType.JSON);
            //发送请求
            client.index(request,RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

    Map<String, List<String>> result = new HashMap<>();

    @Override
    public void deleteById(Long id) {
        try {
            DeleteRequest request = new DeleteRequest("item", id.toString());
            client.delete(request,RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Map<String, List<String>> getFilters(RequestParams requestParams, SearchRequest request) {
        try {
            //设置请求参数
            //按照之前的搜索
            getsearchAll(requestParams,request);
            //搜索到的不显示
            request.source().size(0);
            //按品牌分桶
            request.source().aggregation(AggregationBuilders
            .terms("brandAgg")
            .field("brand")
            .size(100));

            //按类型分桶
            request.source().aggregation(AggregationBuilders
                    .terms("categoryAgg")
                    .field("category")
                    .size(100));
            //发送请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            //解析结果
            Aggregations aggregations = response.getAggregations();
            List<String> brandAgg = getAggByName(aggregations, "brandAgg");
            result.put("brand",brandAgg);
            List<String> categoryAgg = getAggByName(aggregations, "categoryAgg");
            result.put("category",categoryAgg);
        } catch (IOException e) {
            log.error("聚合搜索出错{}", e.getMessage());
        }


        return result;
    }



    @Override
    public List<String> getSuggestions(String key) {
        List<String> list = new ArrayList<>();
        try {
            //1.准备请求
            SearchRequest request = new SearchRequest("item");
            //2.设置参数
            request.source().suggest(new SuggestBuilder().addSuggestion(
                    "suggestions", SuggestBuilders.completionSuggestion("suggestion")
                            .prefix(key)
                            .skipDuplicates(true)
                            .size(10)
            ));
            //3.发起请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            //4.解析结果
            Suggest suggest = response.getSuggest();
            //5.获取补全结果
            CompletionSuggestion suggestions = suggest.getSuggestion("suggestions");
            List<CompletionSuggestion.Entry.Option> options = suggestions.getOptions();
            //6.返回结果

            for (CompletionSuggestion.Entry.Option option : options) {
                String text = option.getText().toString();
                list.add(text);
            }
            return list;
        } catch (IOException e) {
            log.error("获取出错：原因为{}", e.getMessage());
        }
        return null;
    }

    @Override
    public PageDTO<ItemDoc> getsearchAll(RequestParams requestParams, SearchRequest request) {
        PageDTO<ItemDoc> pageResult = null;
        try {
            //1.创建请求对象
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

            //2.设置请求参数  "query": "match": {"all": "手机"}}
            //关键字查询
            String key = requestParams.getKey();
            if (StringUtils.isNotBlank(key)) {
                boolQuery.must(QueryBuilders.matchQuery("all", key));
            } else {
                boolQuery.must(QueryBuilders.matchAllQuery());
            }

            //过滤条件查询  brand category price
            if (StringUtils.isNotBlank(requestParams.getBrand())) {
                boolQuery.filter(QueryBuilders.termQuery("brand", requestParams.getBrand()));
            }
            if (StringUtils.isNotBlank(requestParams.getCategory())) {
                boolQuery.filter(QueryBuilders.termQuery("category", requestParams.getCategory()));
            }
            //价格用range (索引库存的是分不是元)
            if (requestParams.getMinPrice() != null && requestParams.getMinPrice() != null) {
                boolQuery.filter(QueryBuilders.rangeQuery("price")
                        .gte(requestParams.getMinPrice() * 100)
                        .lte(requestParams.getMaxPrice() * 100));
            }

            request.source().query(boolQuery);

            //分页
            int page = requestParams.getPage();
            int size = requestParams.getSize();
            request.source().from((page - 1) * size).size(size);


            //竞价排名
            FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(
                    boolQuery, new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                            new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                    //加分条件
                                    QueryBuilders.termQuery("isAD", true),
                                    //加分权重
                                    ScoreFunctionBuilders.weightFactorFunction(15)
                            )
                    }
            );
            request.source().query(functionScoreQuery);

            //排序 判断sortBy default 默认 score 按评分自定义排序 price 按价格自定义排序
            String sortBy = requestParams.getSortBy();
            if (sortBy.equals("sold")) {
                //评分排序
                request.source().sort("sold", SortOrder.DESC);

            } else if (sortBy.equals("price")) {
                //价格排序
                request.source().sort("price", SortOrder.ASC);
            }

            //高亮 "highlight": {
            //    "fields": {"name": {
            //        "pre_tags": "<em>",
            //        "post_tags": "</em>"}}}
            if (StringUtils.isNotBlank(key)) {
                HighlightBuilder highlightBuilder = new HighlightBuilder();
                highlightBuilder.field("name");
                highlightBuilder.requireFieldMatch(false);
                highlightBuilder.preTags("<em>");
                highlightBuilder.postTags("</em>");
                request.source().highlighter(highlightBuilder);
            }

            //3.发送请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            //获取结果
            pageResult = getRestult(response);

        } catch (IOException e) {
            log.error("查询出错信息：{}", e.getMessage());
        }
        return pageResult;
    }





    /**
     * @功能: 处理返回结果
     * @return: com.hmall.common.dto.PageDTO<com.hmall.search.pojo.ItemDoc>
     */
    private PageDTO<ItemDoc> getRestult(SearchResponse response) {
        //4.解析结果
        SearchHits hits = response.getHits();
        SearchHit[] searchHits = hits.getHits();
        long total = hits.getTotalHits().value;  //总页数
        //遍历 拿到 source字段
        List<ItemDoc> items = new ArrayList<>(); //存储查询返回的酒店信息
        for (SearchHit hit : searchHits) {
            String sourceAsString = hit.getSourceAsString(); //JSON 格式
            ItemDoc itemDoc = JSON.parseObject(sourceAsString, ItemDoc.class); //转换为HotelDoc对象

            //处理高亮结果 hits: highlight:name
            if (hit.getHighlightFields().size() > 0) {
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField highlightField = highlightFields.get("name");
                Text[] fragments = highlightField.fragments();
                String newname = "";
                for (Text fragment : fragments) {
                    newname += fragment;
                }
                itemDoc.setName(newname);
            }
            items.add(itemDoc);
        }
        return new PageDTO<>(total, items);
    }
    /**
     * @功能: 获取聚合结果桶里的数据
     * @return: java.util.List<java.lang.String>
     */
    public List<String> getAggByName(Aggregations aggregations, String aggname) {

        Terms terms = aggregations.get(aggname);

        //获取桶 buckets
        List<? extends Terms.Bucket> buckets = terms.getBuckets();
        List<String> list = new ArrayList<>();
        for (Terms.Bucket bucket : buckets) {
            String key = bucket.getKeyAsString();
            list.add(key);
        }
        return list;
    }
}
